import os
import zipfile
from datetime import datetime
from stat import S_IFREG

from stream_zip import ZIP_32, stream_zip


def read_file_in_chunks(file_path, chunk_size=1024):
    """A generator function that reads a file in chunks and yields each chunk.

    Args:
    file_path (str): The path to the file to be read.
    chunk_size (int): The size of each chunk to read. Default is 1024 bytes.

    Yields:
    bytes: The next chunk of the file.
    """
    with open(file_path, 'rb') as file:
        while True:
            chunk = file.read(chunk_size)
            if not chunk:
                break
            yield chunk


def zip_stream(path, chunk_size=1024):
    """
    打包压缩目录成 zip文件，流式压缩输出到内存，不经过文件中转
    """
    if os.path.isfile(path):
        yield from stream_zip((
            (
                os.path.basename(path),  # File name
                datetime.now(),  # Modification time
                S_IFREG | 0o600,  # Mode - regular file that owner can read and write
                ZIP_32,  # ZIP_32 has good support but limited to 4GiB
                read_file_in_chunks(path, chunk_size=chunk_size)  # Iterable of chunks of contents
            ),
        ))

    all_files = []
    for root, dirs, files in os.walk(path):
        for file in files:
            file_path = os.path.join(root, file)
            all_files.append((
                os.path.relpath(file_path, path),
                datetime.now(),
                S_IFREG | 0o600,
                ZIP_32,
                read_file_in_chunks(file_path, chunk_size=chunk_size)
            ))
    yield from stream_zip(tuple(all_files))


def unzip(filepath, output_dir):
    zip_file = zipfile.ZipFile(filepath)
    for names in zip_file.namelist():
        if names.find('__MACOSX') != -1 or names.find('DS_Store') != -1:
            continue
        zip_file.extract(names, output_dir)